﻿//Security.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.BusinessEntities
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Runtime.Serialization;
	using System.Xml.Serialization;

	using Ecng.Collections;
	using Ecng.Common;
	using Ecng.ComponentModel;
	using Ecng.Serialization;

	using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;

	/// <summary>
	/// Состояния инструмента.
	/// </summary>
	[Serializable]
	[System.Runtime.Serialization.DataContract]
	public enum SecurityStates
	{
		/// <summary>
		/// Торгуется.
		/// </summary>
		[EnumMember]
		Trading,

		/// <summary>
		/// Торги приостановлены.
		/// </summary>
		[EnumMember]
		Stoped,
	}

	/// <summary>
	/// Типы инструментов.
	/// </summary>
	[Serializable]
	[System.Runtime.Serialization.DataContract]
	public enum SecurityTypes
	{
		/// <summary>
		/// Ценная бумага.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Акции")]
		Stock,

		/// <summary>
		/// Фьючерс.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Фьючерс")]
		Future,

		/// <summary>
		/// Опцион.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Опцион")]
		Option,

		/// <summary>
		/// Индекс.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Индекс")]
		Index,

		/// <summary>
		/// Валюта.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Валюта")]
		Currency,

		/// <summary>
		/// Облигация.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Облигация")]
		Bond,

		/// <summary>
		/// Варрант.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Варрант")]
		Warrant,

		/// <summary>
		/// Форвард.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Форвард")]
		Forward,

		/// <summary>
		/// Споп.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Своп")]
		Swap,
	}

	/// <summary>
	/// Типы опционов.
	/// </summary>
	[Serializable]
	[System.Runtime.Serialization.DataContract]
	public enum OptionTypes
	{
		/// <summary>
		/// Кол.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Кол")]
		Call,

		/// <summary>
		/// Пут.
		/// </summary>
		[EnumMember]
		[EnumDisplayName("Пут")]
		Put,
	}

	/// <summary>
	/// Идентификаторы инструмента в других системах.
	/// </summary>
	public class SecurityExternalId
	{
		/// <summary>
		/// Идентификатор в формате SEDOL (Stock Exchange Daily Official List).
		/// </summary>
		[DataMember]
		[DisplayName("SEDOL")]
		[Description("Идентификатор в формате SEDOL (Stock Exchange Daily Official List).")]
		public string Sedol { get; set; }

		/// <summary>
		/// Идентификатор в формате CUSIP (Committee on Uniform Securities Identification Procedures).
		/// </summary>
		[DataMember]
		[DisplayName("CUSIP")]
		[Description("Идентификатор в формате CUSIP (Committee on Uniform Securities Identification Procedures).")]
		public string Cusip { get; set; }

		/// <summary>
		/// Идентификатор в формате ISIN (International Securities Identification Number).
		/// </summary>
		[DataMember]
		[DisplayName("ISIN")]
		[Description("Идентификатор в формате ISIN (International Securities Identification Number).")]
		public string Isin { get; set; }

		/// <summary>
		/// Идентификатор в формате RIC (Reuters Instrument Code).
		/// </summary>
		[DataMember]
		[DisplayName("RIC")]
		[Description("Идентификатор в формате RIC (Reuters Instrument Code).")]
		public string Ric { get; set; }

		/// <summary>
		/// Идентификатор в формате Bloomberg.
		/// </summary>
		[DataMember]
		[DisplayName("Bloomberg")]
		[Description("Идентификатор в формате Bloomberg.")]
		public string Bloomberg { get; set; }

		/// <summary>
		/// Идентификатор в формате IQFeed.
		/// </summary>
		[DataMember]
		[DisplayName("IQFeed")]
		[Description("Идентификатор в формате IQFeed.")]
		public string IQFeed { get; set; }
	}

	/// <summary>
	/// Инструмент (акция, фьючерс, опцион и т.д.).
	/// </summary>
	[Serializable]
	[System.Runtime.Serialization.DataContract]
	[DisplayName("Инструмент")]
	[Description("Инструмент (акция, фьючерс, опцион и т.д.).")]
	public class Security : Equatable<Security>, IExtendableEntity, IAtomic
	{
		private const string _categoryStatistics = "Статистика";
		private const string _categoryMain = "Основные";
		private const string _categoryDerivatives = "Деривативы";

		private readonly TransactionManager _manager = new TransactionManager();

		/// <summary>
		/// Создать объект инструмента.
		/// </summary>
		public Security()
		{
			Name = ShortName = Code = Class = string.Empty;
			UnderlyingSecurityId = string.Empty;
			MinStepPrice = 1;
			MinLotSize = 1;
		}

		private string _id;

		/// <summary>
		/// Идентификатор инструмента.
		/// </summary>
		[DataMember]
		[Identity]
		[DisplayName("Идентификатор")]
		[Description("Идентификатор инструмента.")]
		[Category(_categoryMain)]
		[ReadOnly(true)]
		public string Id
		{
			get { return _id; }
			set
			{
				if (_id == value)
					return;

				_id = value;
				Notify("Id");
			}
		}

		private string _name;

		/// <summary>
		/// Название инструмента.
		/// </summary>
		[DataMember]
		[DisplayName("Название")]
		[Description("Название инструмента.")]
		[Category(_categoryMain)]
		public string Name
		{
			get { return _name; }
			set
			{
				if (_name == value)
					return;

				_name = value;
				Notify("Name");
			}
		}

		private string _shortName;

		/// <summary>
		/// Короткое название инструмента.
		/// </summary>
		[DataMember]
		[DisplayName("Короткое название")]
		[Description("Короткое название инструмента.")]
		[Category(_categoryMain)]
		public string ShortName
		{
			get { return _shortName; }
			set
			{
				if (_shortName == value)
					return;

				_shortName = value;
				Notify("ShortName");
			}
		}

		private string _code;

		/// <summary>
		/// Код инструмента.
		/// </summary>
		[DataMember]
		[DisplayName("Код")]
		[Description("Код инструмента.")]
		[Category(_categoryMain)]
		public string Code
		{
			get { return _code; }
			set
			{
				if (_code == value)
					return;

				_code = value;
				Notify("Code");
			}
		}

		private string _class;

		/// <summary>
		/// Класс инструмента.
		/// </summary>
		[DataMember]
		[DisplayName("Класс")]
		[Description("Класс инструмента.")]
		[Category(_categoryMain)]
		public string Class
		{
			get { return _class; }
			set
			{
				if (_class == value)
					return;

				_class = value;
				Notify("Class");
			}
		}

		private decimal _minStepSize;

		/// <summary>
		/// Минимальный шаг цены.
		/// </summary>
		[DataMember]
		[DisplayName("Шаг")]
		[Description("Минимальный шаг цены.")]
		[Category(_categoryMain)]
		public decimal MinStepSize
		{
			get { return _minStepSize; }
			set
			{
				if (_minStepSize == value)
					return;

				_minStepSize = value;
				Decimals = _minStepSize.GetDecimalInfo().EffectiveScale;
				Notify("MinStepSize");
			}
		}

		private decimal _minStepPrice;

		/// <summary>
		/// Стоимость шага цены.
		/// </summary>
		[DataMember]
		[DisplayName("Стоимость шага")]
		[Description("Стоимость шага цены.")]
		[Category(_categoryMain)]
		public decimal MinStepPrice
		{
			get { return _minStepPrice; }
			set
			{
				if (value < 0)
					throw new ArgumentOutOfRangeException("value", value, "Стоимость шага цены не может быть меньше нуля.");

				if (_minStepPrice == value)
					return;

				_minStepPrice = value;
				Notify("MinStepPrice");
			}
		}

		private int _minLotSize;

		/// <summary>
		/// Минимальный размер лота.
		/// </summary>
		[DataMember]
		[DisplayName("Лот")]
		[Description("Минимальный размер лота.")]
		[Category(_categoryMain)]
		public int MinLotSize
		{
			get { return _minLotSize; }
			set
			{
				if (_minLotSize == value)
					return;

				_minLotSize = value;
				Notify("MinLotSize");
			}
		}

		private int _decimals;

		/// <summary>
		/// Количество знаков в цене после запятой. Автоматически выставляется при присвоении <see cref="Security.MinStepSize"/>.
		/// </summary>
		[DataMember]
		[DisplayName("Точность")]
		[Description("Количество знаков в цене после запятой.")]
		[Category(_categoryMain)]
		public int Decimals
		{
			get { return _decimals; }
			set
			{
				if (_decimals == value)
					return;

				_decimals = value;
				Notify("Decimals");
			}
		}

		private Trade _lastTrade;

		/// <summary>
		/// Информация о последней сделке. Если за сессию по инструменту не было сделок, то значение равно null.
		/// </summary>
		[DataMember]
		//[NameOverride("Id", "LastTradeId")]
		//[NameOverride("Time", "LastTradeTime")]
		//[NameOverride("Price", "LastTradePrice")]
		//[NameOverride("Volume", "LastTradeVolume")]
		//[NameOverride("OrderDirection", "LastTradeOrderDirection")]
		[Ignore]
		[DisplayName("Последняя сделка")]
		[Description("Информация о последней сделке.")]
		[ExpandableObject]
		[Category(_categoryStatistics)]
		public Trade LastTrade
		{
			get { return _lastTrade; }
			set
			{
				if (_lastTrade == value)
					return;

				_lastTrade = value;
				Notify("LastTrade");

				if (value != null && value.Time != default(DateTime))
					LastChangeTime = value.Time;
			}
		}

		private decimal _openPrice;

		/// <summary>
		/// Первая цена сделки за сессию.
		/// </summary>
		[DataMember]
		[DisplayName("Первая цена сделки")]
		[Description("Первая цена сделки за сессию.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal OpenPrice
		{
			get { return _openPrice; }
			set
			{
				if (_openPrice == value)
					return;

				_openPrice = value;
				Notify("OpenPrice");
			}
		}

		private decimal _closePrice;

		/// <summary>
		/// Последняя цена сделки за предыдущую сессию.
		/// </summary>
		[DataMember]
		[DisplayName("Последняя цена сделки")]
		[Description("Последняя цена сделки за предыдущую сессию.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal ClosePrice
		{
			get { return _closePrice; }
			set
			{
				if (_closePrice == value)
					return;

				_closePrice = value;
				Notify("ClosePrice");
			}
		}

		private decimal _lowPrice;

		/// <summary>
		/// Наинизшая цена сделки за сессию.
		/// </summary>
		[DataMember]
		[DisplayName("Наинизшая цена")]
		[Description("Наинизшая цена сделки за сессию.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal LowPrice
		{
			get { return _lowPrice; }
			set
			{
				if (_lowPrice == value)
					return;

				_lowPrice = value;
				Notify("LowPrice");
			}
		}

		private decimal _highPrice;

		/// <summary>
		/// Наивысшая цена сделки за сессию.
		/// </summary>
		[DataMember]
		[DisplayName("Наивысшая цена")]
		[Description("Наивысшая цена сделки за сессию.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal HighPrice
		{
			get { return _highPrice; }
			set
			{
				if (_highPrice == value)
					return;

				_highPrice = value;
				Notify("HighPrice");
			}
		}

		private Quote _bestBid;

		/// <summary>
		/// Лучшая покупка в стакане.
		/// </summary>
		[DataMember]
		//[NameOverride("Price", "BestBidPrice")]
		//[NameOverride("Volume", "BestBidVolume")]
		//[NameOverride("OrderDirection", "BestBidOrderDirection")]
		//[InnerSchema]
		[Ignore]
		[DisplayName("Лучшая покупка")]
		[Description("Лучшая покупка в стакане.")]
		[ExpandableObject]
		[Category(_categoryStatistics)]
		public Quote BestBid
		{
			get { return _bestBid; }
			set
			{
				//TODO: решить другим методом, OnEquals не тормозит, медленно работает GUI
				//PYH: Тормозит OnEquals

				//if (_bestBid == value)
				//	return;

				_bestBid = value;
				Notify("BestBid");
			}
		}

		private Quote _bestAsk;

		/// <summary>
		/// Лучшая продажа в стакане.
		/// </summary>
		[DataMember]
		//[NameOverride("Price", "BestAskPrice")]
		//[NameOverride("Volume", "BestAskVolume")]
		//[NameOverride("OrderDirection", "BestAskOrderDirection")]
		//[InnerSchema]
		[Ignore]
		[DisplayName("Лучшая продажа")]
		[Description("Лучшая продажа в стакане.")]
		[ExpandableObject]
		[Category(_categoryStatistics)]
		public Quote BestAsk
		{
			get { return _bestAsk; }
			set
			{
				// if (_bestAsk == value)
				//	return;

				_bestAsk = value;
				Notify("BestAsk");
			}
		}

		/// <summary>
		/// Лучшая пара котировок.
		/// </summary>
		[DisplayName("Лучшая пара")]
		[Description("Лучшая пара котировок.")]
		[ExpandableObject]
		[Category(_categoryStatistics)]
		public MarketDepthPair BestPair
		{
			get { return new MarketDepthPair(this, BestBid, BestAsk); }
		}

		private SecurityStates _state;

		/// <summary>
		/// Текущее состояние инструмента.
		/// </summary>
		[DataMember]
		[Enum]
		[DisplayName("Состояние")]
		[Description("Текущее состояние инструмента.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public SecurityStates State
		{
			get { return _state; }
			set
			{
				if (_state == value)
					return;

				_state = value;
				Notify("State");
			}
		}

		private SecurityTypes _type;

		/// <summary>
		/// Тип инструмента.
		/// </summary>
		[DataMember]
		[Enum]
		[DisplayName("Тип")]
		[Description("Тип инструмента.")]
		[Category(_categoryMain)]
		public SecurityTypes Type
		{
			get { return _type; }
			set
			{
				if (_type == value)
					return;

				_type = value;
				Notify("Type");
			}
		}

		[field: NonSerialized]
		private SynchronizedDictionary<object, object> _extensionInfo;

		/// <summary>
		/// Расширенная информация по инструменту.
		/// </summary>
		/// <remarks>
		/// Необходима в случае хранения в программе дополнительной информации, ассоциированной с инструментом.
		/// Например, дата экспирации инструмента (если эта опцион) или информация о базовом активе, если это фьючерсный контракт.
		/// </remarks>
		[XmlIgnore]
		[DataMember]
		[DisplayName("Расширенная информация")]
		[Description("Необходима в случае хранения в программе дополнительной информации, ассоциированной с инструментом.")]
		[Category(_categoryMain)]
		public IDictionary<object, object> ExtensionInfo
		{
			get { return _extensionInfo; }
			set { _extensionInfo = value.Sync(); }
		}

		private decimal _minPrice;

		/// <summary>
		/// Нижний лимит цены.
		/// </summary>
		[DataMember]
		[DisplayName("Минимально-возможная цена")]
		[Description("Нижний лимит цены.")]
		[Category(_categoryMain)]
		[Ignore]
		public decimal MinPrice
		{
			get { return _minPrice; }
			set
			{
				if (_minPrice == value)
					return;

				_minPrice = value;
				Notify("MinPrice");
			}
		}

		private decimal _maxPrice;

		/// <summary>
		/// Верхний лимит цены.
		/// </summary>
		[DataMember]
		[DisplayName("Максимально-возможная цена")]
		[Description("Верхний лимит цены.")]
		[Category(_categoryMain)]
		[Ignore]
		public decimal MaxPrice
		{
			get { return _maxPrice; }
			set
			{
				if (_maxPrice == value)
					return;

				_maxPrice = value;
				Notify("MaxPrice");
			}
		}

		private decimal _marginBuy;

		/// <summary>
		/// Гарантийное обеспечение на покупку.
		/// </summary>
		[DataMember]
		[DisplayName("ГО покупка")]
		[Description("Гарантийное обеспечение на покупку.")]
		[Category(_categoryMain)]
		[Ignore]
		public decimal MarginBuy
		{
			get { return _marginBuy; }
			set
			{
				if (_marginBuy == value)
					return;

				_marginBuy = value;
				Notify("MarginBuy");
			}
		}

		private decimal _marginSell;

		/// <summary>
		/// Гарантийное обеспечение на продажу.
		/// </summary>
		[DataMember]
		[DisplayName("ГО продажа")]
		[Description("Гарантийное обеспечение на продажу.")]
		[Category(_categoryMain)]
		[Ignore]
		public decimal MarginSell
		{
			get { return _marginSell; }
			set
			{
				if (_marginSell == value)
					return;

				_marginSell = value;
				Notify("MarginSell");
			}
		}

		private DateTime? _expiryDate;

		/// <summary>
		/// Дата экспирация инструмента (для деривативов - экспирация, для облигаций - погашение).
		/// </summary>
		[DataMember]
		[Nullable]
		[DisplayName("Дата экспирация")]
		[Description("Дата экспирация инструмента (для деривативов - экспирация, для облигаций - погашение).")]
		[Category(_categoryMain)]
		public DateTime? ExpiryDate
		{
			get { return _expiryDate; }
			set
			{
				if (_expiryDate == value)
					return;

				_expiryDate = value;
				Notify("ExpiryDate");
			}
		}

		private DateTime? _settlementDate;

		/// <summary>
		/// Дата выплат по инструмента (для деривативов и облигаций).
		/// </summary>
		[DataMember]
		[Nullable]
		[DisplayName("Дата выплат")]
		[Description("Дата выплат по инструмента (для деривативов и облигаций).")]
		[Category(_categoryMain)]
		public DateTime? SettlementDate
		{
			get { return _settlementDate; }
			set
			{
				if (_settlementDate == value)
					return;

				_settlementDate = value;
				Notify("SettlementDate");
			}
		}

		[field: NonSerialized]
		private ITrader _trader;

		/// <summary>
		/// Шлюз к торговой системе, через который был загружен данный инструмент.
		/// </summary>
		[Ignore]
		[XmlIgnore]
		[Browsable(false)]
		public virtual ITrader Trader
		{
			get { return _trader; }
			set
			{
				if (_trader == value)
					return;

				_trader = value;
				Notify("Trader");
			}
		}

		private ExchangeBoard _exchangeBoard;

		/// <summary>
		/// Биржевая площадка, на которой торгуется инструмент.
		/// </summary>
		[RelationSingle(IdentityType = typeof(string))]
		[DataMember]
		[DisplayName("Площадка")]
		[Description("Биржевая площадка, на которой торгуется инструмент.")]
		[Category(_categoryMain)]
		public ExchangeBoard ExchangeBoard
		{
			get { return _exchangeBoard; }
			set
			{
				if (_exchangeBoard == value)
					return;

				_exchangeBoard = value;
				Notify("ExchangeBoard");
			}
		}

		private string _underlyingSecurityId;

		/// <summary>
		/// Базовый актив, на основе которого построен данный инструмент.
		/// </summary>
		[DataMember]
		[DisplayName("Базовый актив")]
		[Description("Базовый актив, на основе которого построен данный инструмент.")]
		[Category(_categoryDerivatives)]
		public string UnderlyingSecurityId
		{
			get { return _underlyingSecurityId; }
			set
			{
				if (_underlyingSecurityId == value)
					return;

				_underlyingSecurityId = value;
				Notify("UnderlyingSecurityCode");
			}
		}

		private decimal _strike;

		/// <summary>
		/// Страйк цена опциона.
		/// </summary>
		[DataMember]
		[DisplayName("Страйк")]
		[Description("Страйк цена опциона.")]
		[Category(_categoryDerivatives)]
		public decimal Strike
		{
			get { return _strike; }
			set
			{
				if (_strike == value)
					return;

				_strike = value;
				Notify("Strike");
			}
		}

		private OptionTypes? _optionType;

		/// <summary>
		/// Тип опциона.
		/// </summary>
		[DataMember]
		[Nullable]
		[DisplayName("Тип опциона")]
		[Description("Тип опциона.")]
		[Category(_categoryDerivatives)]
		public OptionTypes? OptionType
		{
			get { return _optionType; }
			set
			{
				if (_optionType == value)
					return;

				_optionType = value;
				Notify("OptionType");
			}
		}

		private decimal _impliedVolatility;

		/// <summary>
		/// Волатильность (подразумеваемая).
		/// </summary>
		[DataMember]
		[DisplayName("Волатильность (подразумеваемая)")]
		[Description("Волатильность (подразумеваемая).")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal ImpliedVolatility
		{
			get { return _impliedVolatility; }
			set
			{
				if (_impliedVolatility == value)
					return;

				_impliedVolatility = value;
				Notify("ImpliedVolatility");
			}
		}

		private decimal _historicalVolatility;

		/// <summary>
		/// Волатильность (историческая).
		/// </summary>
		[DataMember]
		[DisplayName("Волатильность (историческая)")]
		[Description("Волатильность (историческая).")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal HistoricalVolatility
		{
			get { return _historicalVolatility; }
			set
			{
				if (_historicalVolatility == value)
					return;

				_historicalVolatility = value;
				Notify("HistoricalVolatility");
			}
		}

		private decimal _theorPrice;

		/// <summary>
		/// Теоретическая цена.
		/// </summary>
		[DataMember]
		[DisplayName("Теоретическая цена")]
		[Description("Теоретическая цена.")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal TheorPrice
		{
			get { return _theorPrice; }
			set
			{
				if (_theorPrice == value)
					return;

				_theorPrice = value;
				Notify("TheorPrice");
			}
		}

		private decimal _delta;

		/// <summary>
		/// Дельта опциона.
		/// </summary>
		[DataMember]
		[DisplayName("Дельта")]
		[Description("Дельта опциона.")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal Delta
		{
			get { return _delta; }
			set
			{
				if (_delta == value)
					return;

				_delta = value;
				Notify("Delta");
			}
		}

		private decimal _gamma;

		/// <summary>
		/// Гамма опциона.
		/// </summary>
		[DataMember]
		[DisplayName("Гамма")]
		[Description("Гамма опциона.")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal Gamma
		{
			get { return _gamma; }
			set
			{
				if (_gamma == value)
					return;

				_gamma = value;
				Notify("Gamma");
			}
		}

		private decimal _vega;

		/// <summary>
		/// Вега опциона.
		/// </summary>
		[DataMember]
		[DisplayName("Вега")]
		[Description("Вега опциона.")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal Vega
		{
			get { return _vega; }
			set
			{
				if (_vega == value)
					return;

				_vega = value;
				Notify("Vega");
			}
		}

		private decimal _theta;

		/// <summary>
		/// Тета опциона.
		/// </summary>
		[DataMember]
		[DisplayName("Тета")]
		[Description("Тета опциона.")]
		[Category(_categoryDerivatives)]
		[Ignore]
		public decimal Theta
		{
			get { return _theta; }
			set
			{
				if (_theta == value)
					return;

				_theta = value;
				Notify("Theta");
			}
		}

		private decimal _openInterest;

		/// <summary>
		/// Количество открытых позиций (открытый интерес).
		/// </summary>
		[DataMember]
		[DisplayName("Открытый интерес")]
		[Description("Количество открытых позиций (открытый интерес).")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal OpenInterest
		{
			get { return _openInterest; }
			set
			{
				if (_openInterest == value)
					return;

				_openInterest = value;
				Notify("OpenInterest");
			}
		}

		private DateTime _lastChangeTime;

		/// <summary>
		/// Время последнего изменения инструмента.
		/// </summary>
		[DataMember]
		[DisplayName("Изменен")]
		[Description("Время последнего изменения инструмента.")]
		[Category(_categoryStatistics)]
		public DateTime LastChangeTime
		{
			get { return _lastChangeTime; }
			set
			{
				_lastChangeTime = value;
				Notify("LastChangeTime");
			}
		}

		private decimal _bidsVolume;

		/// <summary>
		/// Суммарный объем во всех заявках на покупку.
		/// </summary>
		[DataMember]
		[DisplayName("Объем бидов")]
		[Description("Суммарный объем во всех заявках на покупку.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal BidsVolume
		{
			get { return _bidsVolume; }
			set
			{
				_bidsVolume = value;
				Notify("BidsVolume");
			}
		}

		private decimal _bidsCount;

		/// <summary>
		/// Количество заявок на покупку.
		/// </summary>
		[DataMember]
		[DisplayName("Кол-во бидов")]
		[Description("Количество заявок на покупку.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal BidsCount
		{
			get { return _bidsCount; }
			set
			{
				_bidsCount = value;
				Notify("BidsCount");
			}
		}

		private decimal _asksVolume;

		/// <summary>
		/// Суммарный объем во всех заявках на продажу.
		/// </summary>
		[DataMember]
		[DisplayName("Объем офферов")]
		[Description("Суммарный объем во всех заявках на продажу.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal AsksVolume
		{
			get { return _asksVolume; }
			set
			{
				_asksVolume = value;
				Notify("AsksVolume");
			}
		}

		private decimal _asksCount;

		/// <summary>
		/// Количество заявок на продажу.
		/// </summary>
		[DataMember]
		[DisplayName("Кол-во офферов")]
		[Description("Количество заявок на продажу.")]
		[Category(_categoryStatistics)]
		[Ignore]
		public decimal AsksCount
		{
			get { return _asksCount; }
			set
			{
				_asksCount = value;
				Notify("AsksCount");
			}
		}

		private CurrencyTypes _currency = CurrencyTypes.RUB;

		/// <summary>
		/// Валюта торгового инструмента. По-умолчанию равно <see cref="CurrencyTypes.RUB"/>.
		/// </summary>
		[DataMember]
		[DisplayName("Валюта")]
		[Description("Валюта торгового инструмента.")]
		[Category(_categoryMain)]
		public CurrencyTypes Currency
		{
			get { return _currency; }
			set
			{
				_currency = value;
				Notify("Currency");
			}
		}

		private SecurityExternalId _externalId = new SecurityExternalId();

		/// <summary>
		/// Идентификатор инструмента в других системах.
		/// </summary>
		[DataMember]
		[DisplayName("Внешний идентификатор")]
		[Description("Идентификатор инструмента в других системах.")]
		[Category(_categoryMain)]
		[ExpandableObject]
		[InnerSchema(NullWhenAllEmpty = false)]
		public SecurityExternalId ExternalId
		{
			get { return _externalId; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");
				
				_externalId = value;
				Notify("ExternalId");
			}
		}

		[field: NonSerialized]
		private PropertyChangedEventHandler _propertyChanged;

		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
		{
			add { _propertyChanged += value; }
			remove { _propertyChanged -= value; }
		}

		void INotifyPropertyChangedEx.NotifyPropertyChanged(string info)
		{
			if (_manager != null)
				_manager.Do(() => _propertyChanged.SafeInvoke(this, info));
			//_propertyChanged.SafeInvoke(this, info);
		}

		/// <summary>
		/// Получить строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return Id;
		}

		/// <summary>
		/// Рассчитать хеш-код объекта <see cref="Security"/>.
		/// </summary>
		/// <returns>Хеш-код.</returns>
		public override int GetHashCode()
		{
			//if (Id.IsEmpty())
			//	throw new InvalidOperationException("Идентификатор инструмента не заполнен.");

			return Id.GetHashCode();
		}

		/// <summary>
		/// Проверить инструменты на эквивалентность.
		/// </summary>
		/// <param name="other">Другой инструмент, с которым необходимо сравнивать.</param>
		/// <returns>True, если инструменты равны, иначе, false.</returns>
		public override bool Equals(Security other)
		{
			return ReferenceEquals(this, other);
		}

		/// <summary>
		/// Сравнить с другим инструментом.
		/// </summary>
		/// <param name="other">Другой инструмент, с которым необходимо сравнивать.</param>
		/// <returns>Код сравнения.</returns>
		public override int CompareTo(Security other)
		{
			return ReferenceEquals(this, other) ? 0 : string.CompareOrdinal(_id, other._id);
		}

		/// <summary>
		/// Создать копию объекта <see cref="Security"/>.
		/// </summary>
		/// <returns>Копия объекта.</returns>
		public override Security Clone()
		{
			var clone = new Security
			{
				Id = Id,
				Name = Name,
				Type = Type,
				Code = Code,
				Class = Class,
				ShortName = ShortName,
				MinStepPrice = MinStepPrice,
				MinLotSize = MinLotSize,
				MinStepSize = MinStepSize,
				Decimals = Decimals,
				LowPrice = LowPrice,
				HighPrice = HighPrice,
				ClosePrice = ClosePrice,
				OpenPrice = OpenPrice,
				MinPrice = MinPrice,
				MaxPrice = MaxPrice,
				SettlementDate = SettlementDate,
				Trader = Trader,
				State = State,
				ExchangeBoard = ExchangeBoard,
				ExpiryDate = ExpiryDate,
				OptionType = OptionType,
				UnderlyingSecurityId = UnderlyingSecurityId,
				TheorPrice = TheorPrice,
				ImpliedVolatility = ImpliedVolatility,
				HistoricalVolatility = HistoricalVolatility,
				Strike = Strike,
				MarginBuy = MarginBuy,
				MarginSell = MarginSell,
				OpenInterest = OpenInterest,
				BidsCount = BidsCount,
				BidsVolume = BidsVolume,
				AsksCount = AsksCount,
				AsksVolume = AsksVolume,
				Currency = Currency,
				ExtensionInfo = new SynchronizedDictionary<object, object>()
			};

			if (LastTrade != null)
			{
				clone.LastTrade = LastTrade.Clone();
				clone.LastTrade.Security = clone;
			}

			if (BestBid != null)
			{
				clone.BestBid = BestBid.Clone();
				clone.BestBid.Security = clone;
			}

			if (BestAsk != null)
			{
				clone.BestAsk = BestAsk.Clone();
				clone.BestAsk.Security = clone;
			}

			return clone;
		}

		/// <summary>
		/// Начать обновление данных. Окончание обновление данных инициируется через <see cref="UpdateTransaction.Dispose"/>.
		/// </summary>
		/// <returns>Транзакция на изменения данных.</returns>
		public UpdateTransaction BeginUpdate()
		{
			return _manager.BeginUpdate();
		}

		/// <summary>
		/// Начать чтение данных. В период действия транзакции потоки данных не могут изменить внутреннее состояние объекта, из которого читаются данные.
		/// Окончание чтения данных инициируется через <see cref="ReadTransaction.Dispose"/>.
		/// </summary>
		/// <param name="isUpgradable">Можно ли вызвать внутри блокировки на чтение внутреннюю блокировку на запись.</param>
		/// <returns>Транзакция на чтение данных.</returns>
		public ReadTransaction BeginRead(bool isUpgradable = false)
		{
			return _manager.BeginRead(isUpgradable);
		}

		private void Notify(string propName)
		{
			//pyh: переместил в  this.NotifyPropertyChanged чтобы NotifyPropertyChangedExHelper мог отфильтровать лишние вызовы
			
			// mika
			// если инструмент был получен через WCF десериализацию, то конструктор Security не будет вызван
			//if (_manager != null)
			//	_manager.Do(() => NotifyPropertyChangedExHelper.Notify(this, propName));
			//else
			
			NotifyPropertyChangedExHelper.Notify(this, propName);
		}
	}
}
